/***********************************************************************************************************************
*                                                                                                                      *
* SPLASH build system v0.1                                                                                             *
*                                                                                                                      *
* Copyright (c) 2013 Andrew D. Zonenberg                                                                               *
* All rights reserved.                                                                                                 *
*                                                                                                                      *
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the     *
* following conditions are met:                                                                                        *
*                                                                                                                      *
*    * Redistributions of source code must retain the above copyright notice, this list of conditions, and the         *
*      following disclaimer.                                                                                           *
*                                                                                                                      *
*    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the       *
*      following disclaimer in the documentation and/or other materials provided with the distribution.                *
*                                                                                                                      *
*    * Neither the name of the author nor the names of any contributors may be used to endorse or promote products     *
*      derived from this software without specific prior written permission.                                           *
*                                                                                                                      *
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED   *
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL *
* THE AUTHORS BE HELD LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES        *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR       *
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE       *
* POSSIBILITY OF SUCH DAMAGE.                                                                                          *
*                                                                                                                      *
***********************************************************************************************************************/

#ifndef XilinxFPGAToolchain_h
#define XilinxFPGAToolchain_h

class XilinxFPGAToolchain;

typedef BuildFlag<XilinxFPGAToolchain> XilinxFPGAMapFlag;
typedef BuildFlagList<XilinxFPGAMapFlag> XilinxFPGAMapFlagList;


/**
	@brief Specifies the optimization level and goal used for mapping.
 */
class XilinxMapGlobalOptimizationFlag : public XilinxFPGAMapFlag
{
public:
	enum OptMode
	{
		OPT_SPEED,
		OPT_AREA,
		OPT_POWER
	} m_mode;
	bool m_retiming;

	XilinxMapGlobalOptimizationFlag(XilinxMapGlobalOptimizationFlag::OptMode mode, bool retiming);
	virtual ~XilinxMapGlobalOptimizationFlag();
	virtual XilinxFPGAMapFlag* Clone() const;
	virtual std::string toString(const XilinxFPGAToolchain* toolchain);
};

/**
	@brief Specifies that hierarchy should be ignored during mapping
 */
class XilinxMapIgnoreHierarchyFlag : public XilinxFPGAMapFlag
{
public:
	XilinxMapIgnoreHierarchyFlag();
	virtual ~XilinxMapIgnoreHierarchyFlag();
	virtual XilinxFPGAMapFlag* Clone() const;
	virtual std::string toString(const XilinxFPGAToolchain* toolchain);
};

/**
	@brief Specifies that registers should be duplicated to reduce fanout
 */
class XilinxMapRegisterDuplicationFlag : public XilinxFPGAMapFlag
{
public:
	XilinxMapRegisterDuplicationFlag();
	virtual ~XilinxMapRegisterDuplicationFlag();
	virtual XilinxFPGAMapFlag* Clone() const;
	virtual std::string toString(const XilinxFPGAToolchain* toolchain);
};

/**
	@brief Specifies that registers should be duplicated to reduce fanout
 */
class XilinxMapLogicOptimizationFlag : public XilinxFPGAMapFlag
{
public:
	XilinxMapLogicOptimizationFlag();
	virtual ~XilinxMapLogicOptimizationFlag();
	virtual XilinxFPGAMapFlag* Clone() const;
	virtual std::string toString(const XilinxFPGAToolchain* toolchain);
};

/**
	@brief Specifies the LUT combining mode
 */
class XilinxMapLutCombiningFlag : public XilinxFPGAMapFlag
{
public:
	enum LutMode
	{
		LUT_COMBINE_OFF,
		LUT_COMBINE_AREA,
		LUT_COMBINE_AUTO
	} m_mode;

	XilinxMapLutCombiningFlag(XilinxMapLutCombiningFlag::LutMode mode);
	virtual ~XilinxMapLutCombiningFlag();
	virtual XilinxFPGAMapFlag* Clone() const;
	virtual std::string toString(const XilinxFPGAToolchain* toolchain);
};

/**
	@brief Specifies how densely to pack registers in buses.
 */
class XilinxMapRegisterPackingFlag : public XilinxFPGAMapFlag
{
public:

	XilinxMapRegisterPackingFlag(int buswidth);
	virtual ~XilinxMapRegisterPackingFlag();
	virtual XilinxFPGAMapFlag* Clone() const;
	virtual std::string toString(const XilinxFPGAToolchain* toolchain);
	
	int m_buswidth;
};

/**
	@brief Specifies how densely to pack registers in buses.
 */
class XilinxMapPlacementSeedFlag : public XilinxFPGAMapFlag
{
public:

	XilinxMapPlacementSeedFlag(int seed = 1);
	virtual ~XilinxMapPlacementSeedFlag();
	virtual XilinxFPGAMapFlag* Clone() const;
	virtual std::string toString(const XilinxFPGAToolchain* toolchain);
	
	int m_seed;
};

/**
	@brief Specifies the bitstream should be generated in quad SPI mode.
 */
class XilinxSPIBusWidthFlag : public FPGABitstreamGenerationFlag
{
public:

	XilinxSPIBusWidthFlag(int buswidth);
	virtual ~XilinxSPIBusWidthFlag();
	virtual FPGABitstreamGenerationFlag* Clone() const;
	virtual std::string toString(const FPGAToolchain* toolchain);
	
	int m_buswidth;
};

class XilinxFPGAToolchain : public FPGAToolchain
{
public:
	XilinxFPGAToolchain();
	XilinxFPGAToolchain(int major, int minor);
	
	virtual ~XilinxFPGAToolchain();
	
	/**
		@brief Caching wrapper around CreateDefaultToolchain(), never calls it more than once.
	 */
	static XilinxFPGAToolchain* CreateDefaultToolchainCached();
	
	virtual std::string GetDescription() const;

	///@brief Returns a SHA256 hash uniquely identifying this toolchain version
	virtual std::string GetHash() const;
	
	///@brief Compiles one or more source files to a native simulation executable.
	virtual ClusterJob* CompileSourcesToNativeSimulation(
		std::vector<std::string> source_files,
		std::string output_filename,
		const std::vector<ClusterJob*>& deps,
		std::string top_level,
		std::string device,
		Cluster* cluster
		);
		
	///@brief Synthesizes one or more source files to an unplaced binary netlist (in toolchain-dependent format)
	virtual ClusterJob* SynthesizeSourcesToUnplacedNetlist(
		std::vector<std::string> source_files,
		std::string output_filename,
		const std::vector<ClusterJob*>& deps,
		std::string top_level,
		std::string device,
		FPGASynthesisFlagList flags,
		Cluster* cluster
		);
		
	/**
		@brief Translates an NGC netlist into a NGD netlist.
		
		Xilinx-specific operation, does not appear to have any equivalent in other vendors' workflows.
	 */
	virtual ClusterJob* TranslateNGCNetlistToNGDNetlist(
		std::string ngcfile,
		std::string constraints,
		std::string output_filename,
		const std::vector<ClusterJob*>& deps,
		std::string device,
		Cluster* cluster
		);
		
	/**
		@brief Maps an NGD netlist to a placed, but not routed, NCD netlist.
		
		Xilinx-specific operation, does not appear to have any equivalent in other vendors' workflows.
	 */
	virtual ClusterJob* MapNetlist(
		std::string ngdfile,
		std::string output_filename,
		const std::vector<ClusterJob*>& deps,
		std::string device,
		XilinxFPGAMapFlagList flags,
		Cluster* cluster
		);
		
	///@brief Place and route an unplaced netlist.
	virtual ClusterJob* PlaceAndRouteNetlist(
		std::string input_netlist,
		std::string constraints,
		std::string output_filename,
		std::string device,
		const std::vector<ClusterJob*>& deps,
		FPGAPlaceAndRouteFlagList flags,
		Cluster* cluster
		);
		
	///@brief Run static timing analysis on a placed netlist
	virtual ClusterJob* StaticTimingAnalysis(
		std::string input_netlist,
		std::string constraints,
		std::string output_filename,
		int speedgrade,
		const std::vector<ClusterJob*>& deps,
		FPGATimingAnalysisFlagList flags,
		Cluster* cluster
		);
		
	///@brief Generates a bitstream from a placed netlist
	virtual ClusterJob* GenerateBitstream(
		std::string input_netlist,
		std::string constraints,
		std::string output_filename,
		std::string device,
		const std::vector<ClusterJob*>& deps,
		FPGABitstreamGenerationFlagList flags,
		Cluster* cluster
		);
		
	///@brief Fits an NGD (or equivalent) netlist to the target CPLD
	virtual ClusterJob* FitCPLDNetlist(
		std::string input_netlist,
		std::string output_filename,
		std::string device,
		const std::vector<ClusterJob*>& deps,
		Cluster* cluster
		);
		
	//TODO: Consider merging this into the other GenerateBitstream() class
	///@brief Generates a bitstream from a placed netlist
	virtual ClusterJob* GenerateCPLDBitstream(
		std::string input_netlist,
		std::string output_filename,
		const std::vector<ClusterJob*>& deps,
		Cluster* cluster
		);
		
	virtual std::string GetSynthesisFlagHash(const FPGASynthesisFlagList& flags);

	virtual std::string GetArchitecture(std::string device) const;

protected:

	void Init(int major, int minor);
	
	/**
		@brief Creates a XilinxFPGAToolchain object for the most recent toolchain found.
	 */
	static XilinxFPGAToolchain* CreateDefaultToolchain();

protected:
	std::string m_rootpath;
	std::string m_fusepath;
	std::string m_xstpath;
	std::string m_ngdbuildpath;
	std::string m_mappath;
	std::string m_parpath;
	std::string m_trcepath;
	std::string m_bitgenpath;
	std::string m_cpldfitpath;
	std::string m_hprep6path;
};

#endif
